package com.sangmo.fts.common.authority.aspect;

import com.sangmo.boot.framework.common.errors.ServiceException;
import com.sangmo.boot.framework.common.errors.StandardErrors;
import com.sangmo.boot.framework.common.utils.ApplicationUtils;
import com.sangmo.boot.framework.common.utils.BeanUtils;
import com.sangmo.boot.framework.common.utils.ExpressionUtils;
import com.sangmo.boot.framework.data.repo.IRepo;
import com.sangmo.fts.common.authority.AuthorityFilterChain;
import com.sangmo.fts.common.authority.EntityContext;
import com.sangmo.fts.common.authority.meta.AuthorityCheckAnn;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

@Aspect
public class AuthorityResourceAspect {

    @Around(value = "@annotation(authorityCheck)")
    public Object around(ProceedingJoinPoint point, AuthorityCheckAnn authorityCheck) throws Throwable {

        if (!AuthorityFilterChain.getInstance().shouldSkipAuthorityCheck()) {
            MethodSignature signature = (MethodSignature) point.getSignature();
            Map<String, Object> parameters = new HashMap();

            String[] names = signature.getParameterNames();
            for (int i = 0; i < names.length; i++) {
                parameters.put(names[i], point.getArgs()[i]);
            }

            Object id = ExpressionUtils.readValue(authorityCheck.indicator(), parameters);

            Object[] ids = null;
            if (id instanceof Collection) {
                ids = ((Collection)id).toArray();
            } else {
                ids = new Object[] {id};
            }

            this.checkRight(authorityCheck.expectedRight(), authorityCheck.authorityClass(), authorityCheck.authorityIndicator(), authorityCheck.resource(), ids);
            AuthorityFilterChain.getInstance().setAuthorityChecked();
        }

        return point.proceed();
    }

    private void checkRight(int expectedRight, Class authorityClass, String authorityIndicator, Class clazz, Object... ids) {
        for (Object id : ids) {
            EntityContext context = null;
            if (authorityClass != Void.class && null != authorityClass) {
                IRepo repo = ApplicationUtils.getBean(IRepo.class, clazz);
                Object obj = repo.getOne((String)id);

                Long newId = (Long) BeanUtils.getProperty(obj, authorityIndicator);
                context = new EntityContext(authorityClass, newId);
            } else {
                context = new EntityContext(clazz, (Long)id);
            }
            boolean pass = AuthorityFilterChain.getInstance().doObjectFilters(context, expectedRight);

            if (!pass) {
                throw new ServiceException(StandardErrors.AUTHORIZED_WRONG.getStatus(), "error.authority.resource.noright").setNamespace(clazz.getSimpleName());
            }
        }
    }
}
